#ifndef __POPUP_CLIENT_HPP__
#define __POPUP_CLIENT_HPP__

#include <PopupClientUI.hpp>
#include <PopupOSAL.hpp>
#include <msg/PopupAbstractMessage.hpp>
#include "PopupSSL.hpp"
#include <queue>
#include <set>

class SocketClient;

namespace Popup
{
  class ClientImpl: public Client,
  public PopupUtils::Socket::KeepAliveHelper
  {
  public:

    ClientImpl(ClientUI *p_clientUI);
    virtual ~ClientImpl();

    //! @Override
    virtual bool sendMessage(UserMessage & p_message);

    //! @Override
    virtual bool sendMessageReaction(const RateUserMessage & p_message);

    //! @Override
    virtual bool submitCanvass(Canvass & p_canvass);

    //! @Override
    virtual bool sendVote(const Vote & p_vote);

    //! @Override
    virtual bool sendInvitation(const ThreadInvitation & p_invitation);

    //!@ Override
    virtual bool notifyUserUpdate(const User *p_myself,
                                  unsigned short p_updateMask);

    //!@ Override
    virtual ConnectionEcode connect(const std::string & p_server,
                                    unsigned short p_port,
                                    const std::string & p_login,
                                    const std::string & p_password,
                                    bool p_register = false);

    //!@ Override
    virtual bool disconnect();

    //!@ Override // Keep alive helper
    virtual bool sendProbe();

  private:
    ConnectionEcode doHandshake(const std::string & p_server,
                                unsigned short p_port);
    ConnectionEcode authenticate(const std::string & p_login,
                                 const std::string & p_password,
                                 bool p_register = false);
    ConnectionEcode listen();

    void processUserUpdate(const UserUpdateMsg & p_msg);
    void processUserMessage(const UserMessageWrapper & p_msg);
    void processAttachment(const AttachmentMsg & p_msg);
    void attachmentSenderRoutine();
    static void *startAttachmentSenderRoutine(void *p_args);
    bool threadSafeMessageSend(AbstractMessage & p_message);
    bool disconnectNoLock();

  private:

    struct MessageWithAttachments
    {
      UserMessage message;
      std::set<AttachmentID> attachmentIDs;
      MessageWithAttachments(const UserMessage & p_msg,
                             const std::set<AttachmentID> & p_attachmentIDs)
      : message(p_msg), attachmentIDs(p_attachmentIDs) {}
      bool isComplete() const;
      void setAttachmentReceived(AttachmentID p_attachmentID,
                                 const std::string & p_filepath);
    };

    typedef std::map<UserID, User*> UserMap;
    typedef std::map<MessageID, MessageWithAttachments*> RecvMessageMap;
    typedef std::queue<FileTransferExt*> SendFileQueue;
    typedef std::map<AttachmentID, FileTransferExt*> RecvFileMap;
    typedef std::map<CanvassID, Canvass*> CanvassMap;

    typedef enum {
      CLIENT_STATE_DISCONNECTED,
      CLIENT_STATE_CONNECTING,
      CLIENT_STATE_CONNECTED,
      CLIENT_STATE_DISCONNECT
    } ConnectionState;

    PopupUtils::Mutex   mutex;
    ClientUI*           clientUI;
    PopupUtils::Socket* socket;
    ConnectionState     state;
    SslContext          sslContext;
    SessionID           sessionID;
    MessageNetworker*   networker;
    std::string         server;
    unsigned short      port;
    unsigned int        threadCounter;
    unsigned int        messageCounter;
    unsigned int        sendFileCounter;
    User                user;
    UserMap             users;
    RecvFileMap         recvFileMap;
    RecvMessageMap      recvMessageMap;
    SendFileQueue       sendFileQueue;
    CanvassMap          canvassMap;
  };
}

#endif // __POPUP_CLIENT_HPP__
